package com.redispro.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.redispro.dto.Result;
import com.redispro.entity.VoucherOrder;
import com.redispro.mapper.*;
import com.redispro.service.*;
import com.redispro.utils.*;
import org.redisson.api.*;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.connection.stream.*;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.*;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 李力
 * @since 2022-12-22
 */
@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {

    @Autowired
    private ISeckillVoucherService seckillVoucherService;

    @Autowired
    private RedisIDUtil idUtil;

    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private SeckillVoucherMapper mapper;

    @Autowired
    private RedissonClient redissonClient;
    //悲观锁-超卖问题
//    @Override
//    @Transactional(rollbackFor = Exception.class)
//    public Result seckillVoucher(Long voucherId) {
//        synchronized (VoucherOrderServiceImpl.class) {
//            if (voucherId == null) {
//                return Result.fail("参数为null");
//            }
//            //1.查询优惠券
//            SeckillVoucher seckillVoucher = seckillVoucherService.getById(voucherId);
//            if (seckillVoucher == null) {
//                return Result.fail("无对应的优惠券");
//            }
//            //2.判断秒杀是否开始
//            if (seckillVoucher.getBeginTime().after(new Date())) {
//                return Result.fail("活动尚未开始");
//            }
//            //3.判断活动是否结束
//            if (seckillVoucher.getEndTime().before(new Date())) {
//                return Result.fail("活动已经结束");
//            }
//            //4.判断库存是否充足
//            if (seckillVoucher.getStock() <= 1) {
//                return Result.fail("库存不足");
//            }
//            //5.扣减库存
//            boolean updateResult = seckillVoucherService.update().setSql("stock=stock-1").eq("voucher_id", voucherId).update();
//            if (!updateResult) {
//                return Result.fail("扣减库存失败");
//            }
//            //6.创建订单
//            VoucherOrder voucherOrder = new VoucherOrder();
//            long orderId = idUtil.genericId("orderId:");
//            voucherOrder.setId(orderId);
//            //方便jmeter测试，用户ID暂时写死
//            voucherOrder.setUserId(100L);
//
//            voucherOrder.setVoucherId(voucherId);
//            boolean save = save(voucherOrder);
//            return save ? Result.ok(orderId) : Result.fail("抢单失败");
//        }
//    }


    //乐观锁-版本号-超卖问题
//    @Override
//    @Transactional(rollbackFor = Exception.class)
//    public Result seckillVoucher(Long voucherId) {
//        if (voucherId == null) {
//            return Result.fail("参数为null");
//        }
//        //1.查询优惠券
//        SeckillVoucher seckillVoucher = seckillVoucherService.getById(voucherId);
//        if (seckillVoucher == null) {
//            return Result.fail("无对应的优惠券");
//        }
//        //2.判断秒杀是否开始
//        if (seckillVoucher.getBeginTime().after(new Date())) {
//            return Result.fail("活动尚未开始");
//        }
//        //3.判断活动是否结束
//        if (seckillVoucher.getEndTime().before(new Date())) {
//            return Result.fail("活动已经结束");
//        }
//        //4.判断库存是否充足
//        if (seckillVoucher.getStock() < 1) {
//            return Result.fail("库存不足");
//        }
//        //5.扣减库存
//        seckillVoucher.setStock(seckillVoucher.getStock() - 1);
//        int updateResult = mapper.updateById(seckillVoucher);
//        if (updateResult <= 0) {
//            return Result.fail("扣减库存失败");
//        }
//        //6.创建订单
//        VoucherOrder voucherOrder = new VoucherOrder();
//        long orderId = idUtil.genericId("orderId:");
//        voucherOrder.setId(orderId);
//        //方便jmeter测试，用户ID暂时写死
//        voucherOrder.setUserId(100L);
//
//        voucherOrder.setVoucherId(voucherId);
//        boolean save = save(voucherOrder);
//        return save ? Result.ok(orderId) : Result.fail("抢单失败");
//    }


    //乐观锁-CAS-超卖问题
//    @Override
//    @Transactional(rollbackFor = Exception.class)
//    public Result seckillVoucher(Long voucherId) {
//        if (voucherId == null) {
//            return Result.fail("参数为null");
//        }
//        //1.查询优惠券
//        SeckillVoucher seckillVoucher = seckillVoucherService.getById(voucherId);
//        if (seckillVoucher == null) {
//            return Result.fail("无对应的优惠券");
//        }
//        //2.判断秒杀是否开始
//        if (seckillVoucher.getBeginTime().after(new Date())) {
//            return Result.fail("活动尚未开始");
//        }
//        //3.判断活动是否结束
//        if (seckillVoucher.getEndTime().before(new Date())) {
//            return Result.fail("活动已经结束");
//        }
//        //4.判断库存是否充足
//        if (seckillVoucher.getStock() <= 1) {
//            return Result.fail("库存不足");
//        }
//        //5.扣减库存
//        boolean updateResult = seckillVoucherService.update().
//                setSql("stock=stock-1").
//                eq("voucher_id", voucherId).
//                gt("stock", 0).
//                update();
//        if (!updateResult) {
//            return Result.fail("扣减库存失败");
//        }
//        //6.创建订单
//        VoucherOrder voucherOrder = new VoucherOrder();
//        long orderId = idUtil.genericId("orderId:");
//        voucherOrder.setId(orderId);
//        //方便jmeter测试，用户ID暂时写死
//        voucherOrder.setUserId(100L);
//        voucherOrder.setVoucherId(voucherId);
//        boolean save = save(voucherOrder);
//        return save ? Result.ok(orderId) : Result.fail("抢单失败");
//    }


    //一人一单问题:synchronized实现
//    @Override
//    public Result seckillVoucher(Long voucherId) {
//        if (voucherId == null) {
//            return Result.fail("参数为null");
//        }
//        //1.查询优惠券
//        SeckillVoucher seckillVoucher = seckillVoucherService.getById(voucherId);
//        if (seckillVoucher == null) {
//            return Result.fail("无对应的优惠券");
//        }
//        //2.判断秒杀是否开始
//        if (seckillVoucher.getBeginTime().after(new Date())) {
//            return Result.fail("活动尚未开始");
//        }
//        //3.判断活动是否结束
//        if (seckillVoucher.getEndTime().before(new Date())) {
//            return Result.fail("活动已经结束");
//        }
//        //4.判断库存是否充足
//        if (seckillVoucher.getStock() <= 1) {
//            return Result.fail("库存不足");
//        }
//        Long id = UserHolder.getUser().getId();
//        //这个地方补充一人一单问题
//
//        //获取事务代理对象
//        IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();
//        synchronized (id.toString().intern()) {
//            return proxy.getResult(id, voucherId);
//        }
//    }

//    @Override
//    public Result seckillVoucher(Long voucherId) {
//        if (voucherId == null) {
//            return Result.fail("参数为null");
//        }
//        //1.查询优惠券
//        SeckillVoucher seckillVoucher = seckillVoucherService.getById(voucherId);
//        if (seckillVoucher == null) {
//            return Result.fail("无对应的优惠券");
//        }
//        //2.判断秒杀是否开始
//        if (seckillVoucher.getBeginTime().after(new Date())) {
//            return Result.fail("活动尚未开始");
//        }
//        //3.判断活动是否结束
//        if (seckillVoucher.getEndTime().before(new Date())) {
//            return Result.fail("活动已经结束");
//        }
//        //4.判断库存是否充足
//        if (seckillVoucher.getStock() <= 1) {
//            return Result.fail("库存不足");
//        }
//        Long id = UserHolder.getUser().getId();
//        //初步采用redis锁实现一人一单
//        SimpleRedisLock redisLock = new SimpleRedisLock("order:" + id, redisTemplate);
//        try {
//            boolean tryLock = redisLock.tryLock(10);
//            if (!tryLock) {
//                return Result.fail("请勿重复下单");
//            }
//            IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();
//            return proxy.getResult(id, voucherId);
//        } catch (Exception exception) {
//            exception.printStackTrace();
//        } finally {
//            //释放锁
//            redisLock.unlock();
//        }
//        return null;
//    }
//
    ////使用lua脚本优化分布式锁
//    @Override
//    @Transactional
//    public Result getResult(Long userId, Long voucherId) {
//        Long count = query().eq("user_id", userId).eq("voucher_id", voucherId).count();
//        if (count > 0) {
//            return Result.fail("不能重复下单");
//        }
//        //5.扣减库存
//        boolean updateResult = seckillVoucherService.update().
//                setSql("stock=stock-1").
//                eq("voucher_id", voucherId).
//                gt("stock", 0).
//                update();
//        if (!updateResult) {
//            return Result.fail("扣减库存失败");
//        }
//        //6.创建订单
//        VoucherOrder voucherOrder = new VoucherOrder();
//        long orderId = idUtil.genericId("orderId:");
//        voucherOrder.setId(orderId);
//        voucherOrder.setUserId(userId);
//        voucherOrder.setVoucherId(voucherId);
//        boolean save = save(voucherOrder);
//        return save ? Result.ok(orderId) : Result.fail("抢单失败");
//    }


    //redisson初步实现分布式锁
//    @Override
//    public Result seckillVoucher(Long voucherId) {
//        if (voucherId == null) {
//            return Result.fail("参数为null");
//        }
//        //1.查询优惠券
//        SeckillVoucher seckillVoucher = seckillVoucherService.getById(voucherId);
//        if (seckillVoucher == null) {
//            return Result.fail("无对应的优惠券");
//        }
//        //2.判断秒杀是否开始
//        if (seckillVoucher.getBeginTime().after(new Date())) {
//            return Result.fail("活动尚未开始");
//        }
//        //3.判断活动是否结束
//        if (seckillVoucher.getEndTime().before(new Date())) {
//            return Result.fail("活动已经结束");
//        }
//        //4.判断库存是否充足
//        if (seckillVoucher.getStock() <= 1) {
//            return Result.fail("库存不足");
//        }
//        Long id = UserHolder.getUser().getId();
//        //初步采用redis锁实现一人一单
//        RLock lock = redissonClient.getLock("lock:order" + id);
//        try {
//            boolean tryLock = lock.tryLock(3, TimeUnit.SECONDS);
//            if (!tryLock) {
//                return Result.fail("请勿重复下单");
//            }
//            IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();
//            return proxy.getResult(id, voucherId);
//        } catch (Exception exception) {
//            exception.printStackTrace();
//        } finally {
//            //释放锁
//            lock.unlock();
//        }
//        return null;
//    }
//
//    //基于jdk阻塞队列+lua脚本实现
//    @Override
//    @Transactional
//    public Result getResult(Long userId, Long voucherId) {
//        Long count = query().eq("user_id", userId).eq("voucher_id", voucherId).count();
//        if (count > 0) {
//            return Result.fail("不能重复下单");
//        }
//        //5.扣减库存
//        boolean updateResult = seckillVoucherService.update().
//                setSql("stock=stock-1").
//                eq("voucher_id", voucherId).
//                gt("stock", 0).
//                update();
//        if (!updateResult) {
//            return Result.fail("扣减库存失败");
//        }
//        //6.创建订单
//        VoucherOrder voucherOrder = new VoucherOrder();
//        long orderId = idUtil.genericId("orderCount");
//        voucherOrder.setId(orderId);
//        voucherOrder.setUserId(userId);
//        voucherOrder.setVoucherId(voucherId);
//        boolean save = save(voucherOrder);
//        return save ? Result.ok(orderId) : Result.fail("抢单失败");
//    }
//
//    private static final DefaultRedisScript<Long> SECKILL_SCRIPT;
//
//    static {
//        SECKILL_SCRIPT = new DefaultRedisScript<>();
//        SECKILL_SCRIPT.setLocation(new ClassPathResource("seckill.lua"));
//        SECKILL_SCRIPT.setResultType(Long.class);
//    }
//
//    private BlockingQueue<VoucherOrder> orderTasks = new ArrayBlockingQueue<>(1024 * 1024);
//
//    private static final ExecutorService EXECUTOR_SERVICE = Executors.newSingleThreadExecutor();
//
//    private IVoucherOrderService proxy;
//
//    @PostConstruct
//    private void init() {
//        EXECUTOR_SERVICE.submit(() -> {
//            while (true) {
//                try {
//                    VoucherOrder voucherOrder = orderTasks.take();
//                    //创建订单
//                    handleOrder(voucherOrder);
//                } catch (InterruptedException e) {
//                    throw new RuntimeException(e);
//                }
//            }
//        });
//    }
//
//    @Override
//    public Result seckillVoucher(Long voucherId) {
//        Long userId = UserHolder.getUser().getId();
//        //1.执行lua脚本
//        Long result = redisTemplate.execute(
//                SECKILL_SCRIPT, Collections.emptyList(), voucherId.toString(), userId.toString());
//        //2.判断结果是否是0
//        int r = result.intValue();
//        if (r != 0) {
//            return Result.fail(r == 1 ? "库存不足" : "不能重复下单");
//        }
//        long orderId = idUtil.genericId("order");
//        //3.保存下单信息到阻塞队列
//        VoucherOrder voucherOrder = new VoucherOrder();
//        voucherOrder.setId(orderId);
//        voucherOrder.setUserId(userId);
//        voucherOrder.setVoucherId(voucherId);
//        orderTasks.add(voucherOrder);
//        //4.异步下单
//        proxy = (IVoucherOrderService) AopContext.currentProxy();
//        //5.返回订单信息
//        return Result.ok(orderId);
//    }
//
//    private Result handleOrder(VoucherOrder voucherOrder) {
//        //初步采用redis锁实现一人一单
//        RLock lock = redissonClient.getLock("lock:order" + voucherOrder.getUserId());
//        try {
//            //当然这个地方其实不用上锁
//            boolean tryLock = lock.tryLock(3, TimeUnit.SECONDS);
//            if (tryLock) {
//                return proxy.getResult2(voucherOrder);
//            }
//        } catch (Exception exception) {
//            exception.printStackTrace();
//        } finally {
//            //释放锁
//            lock.unlock();
//        }
//        return null;
//    }
//
//    @Override
//    @Transactional
//    public Result getResult2(VoucherOrder voucherOrder) {
//        boolean updateResult = seckillVoucherService.update().
//                setSql("stock=stock-1").
//                eq("voucher_id", voucherOrder.getVoucherId()).
//                gt("stock", 0).
//                update();
//        if (!updateResult) {
//            return Result.fail("扣减库存失败");
//        }
//        boolean save = save(voucherOrder);
//        return save ? Result.ok(voucherOrder.getId()) : Result.fail("抢单失败");
//    }

    //基于stream和线程池异步秒杀实现
//    @Override
//    @Transactional
//    public Result getResult(Long userId, Long voucherId) {
//        Long count = query().eq("user_id", userId).eq("voucher_id", voucherId).count();
//        if (count > 0) {
//            return Result.fail("不能重复下单");
//        }
//        //5.扣减库存
//        boolean updateResult = seckillVoucherService.update().
//                setSql("stock=stock-1").
//                eq("voucher_id", voucherId).
//                gt("stock", 0).
//                update();
//        if (!updateResult) {
//            return Result.fail("扣减库存失败");
//        }
//        //6.创建订单
//        VoucherOrder voucherOrder = new VoucherOrder();
//        long orderId = idUtil.genericId("orderCount");
//        voucherOrder.setId(orderId);
//        voucherOrder.setUserId(userId);
//        voucherOrder.setVoucherId(voucherId);
//        boolean save = save(voucherOrder);
//        return save ? Result.ok(orderId) : Result.fail("抢单失败");
//    }
//
    private static final DefaultRedisScript<Long> SECKILL_SCRIPT;

    static {
        SECKILL_SCRIPT = new DefaultRedisScript<>();
        SECKILL_SCRIPT.setLocation(new ClassPathResource("seckill.lua"));
        SECKILL_SCRIPT.setResultType(Long.class);
    }

    private static final ExecutorService EXECUTOR_SERVICE = Executors.newSingleThreadExecutor();

    private IVoucherOrderService proxy;

    @PostConstruct
    private void init() {
        EXECUTOR_SERVICE.submit(() -> {
            while (true) {
                try {
                    //1.获取消息队列中的订单信息
                    List<MapRecord<String, Object, Object>> read = redisTemplate.opsForStream().read(
                            Consumer.from("g1", "c1"),
                            StreamReadOptions.empty().count(1).block(Duration.ofSeconds(2)),
                            StreamOffset.create("streams.order", ReadOffset.lastConsumed())
                    );
                    //2.判断消息,是否获取成功
                    if (read == null || read.isEmpty()) {
                        continue;
                    }
                    //3.获取成功，可以下单:String，消息的id
                    MapRecord<String, Object, Object> entries = read.get(0);
                    Map<Object, Object> value = entries.getValue();
                    VoucherOrder voucherOrder = BeanUtil.fillBeanWithMap(value, new VoucherOrder(), true);
                    handleOrder(voucherOrder);
                    //4.ack确认
                    redisTemplate.opsForStream().acknowledge(
                            "g1", "streams.order", entries.getId()
                    );
                } catch (Exception e) {
                    handlePendingList();
                    throw new RuntimeException(e);
                }
            }
        });
    }

    private void handlePendingList() {
        while (true) {
            try {

                List<MapRecord<String, Object, Object>> read = redisTemplate.opsForStream().read(
                        Consumer.from("g1", "c1"),
                        StreamReadOptions.empty().count(1),
                        StreamOffset.create("streams.order", ReadOffset.from("0"))
                );
                //2.没有异常消息
                if (read == null || read.isEmpty()) {
                    break;
                }
                //3.获取成功，可以下单:String，消息的id
                MapRecord<String, Object, Object> entries = read.get(0);
                Map<Object, Object> value = entries.getValue();
                VoucherOrder voucherOrder = BeanUtil.fillBeanWithMap(value, new VoucherOrder(), true);
                handleOrder(voucherOrder);
                //4.ack确认
                redisTemplate.opsForStream().acknowledge(
                        "g1", "streams.order", entries.getId()
                );
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    @Override
    public Result seckillVoucher(Long voucherId) {
        Long userId = UserHolder.getUser().getId();
        long orderId = idUtil.genericId("order");
        //1.执行lua脚本
        Long result = redisTemplate.execute(
                SECKILL_SCRIPT, Collections.emptyList(), voucherId.toString(), userId.toString(), String.valueOf(orderId));
        //2.判断结果是否是0
        int r = result.intValue();
        if (r != 0) {
            return Result.fail(r == 1 ? "库存不足" : "不能重复下单");
        }
        //4.异步下单
        proxy = (IVoucherOrderService) AopContext.currentProxy();
        //5.返回订单信息
        return Result.ok(orderId);
    }

    private Result handleOrder(VoucherOrder voucherOrder) {
        //初步采用redis锁实现一人一单
        RLock lock = redissonClient.getLock("lock:order" + voucherOrder.getUserId());
        try {
            //当然这个地方其实不用上锁
            boolean tryLock = lock.tryLock(3, TimeUnit.SECONDS);
            if (tryLock) {
                return proxy.getResult2(voucherOrder);
            }
        } catch (Exception exception) {
            exception.printStackTrace();
        } finally {
            //释放锁
            lock.unlock();
        }
        return null;
    }

    @Override
    @Transactional
    public Result getResult2(VoucherOrder voucherOrder) {
        boolean updateResult = seckillVoucherService.update().
                setSql("stock=stock-1").
                eq("voucher_id", voucherOrder.getVoucherId()).
                gt("stock", 0).
                update();
        if (!updateResult) {
            return Result.fail("扣减库存失败");
        }
        boolean save = save(voucherOrder);
        return save ? Result.ok(voucherOrder.getId()) : Result.fail("抢单失败");
    }

    @Override
    public Result getResult(Long id, Long voucherId) {
        return null;
    }
}